Skip to content

fix: separate filter scan height from synced height#442

Merged
xdustinface merged 2 commits intov0.42-devfrom
fix/filter-commited-height
Feb 17, 2026
Merged

fix: separate filter scan height from synced height#442
xdustinface merged 2 commits intov0.42-devfrom
fix/filter-commited-height

Conversation

@xdustinface
Copy link
Collaborator

@xdustinface xdustinface commented Feb 16, 2026

Introduces filter_committed_height on WalletInterface, separate from synced_height. process_block updates synced_height per-block (needed for balance/maturity calculations), but FiltersManager uses filter_committed_height for restart recovery. This prevents a bug where per-block synced_height advances past uncommitted filter batches, causing the rescan on restart to skip heights that need rescanning for newly discovered addresses.

It's not the best solution I think, hence I added the TODO for now. I will look into this at some point later.

Summary by CodeRabbit

  • Refactor
    • Improved wallet restart recovery by shifting progress tracking to a checkpointed filter-committed metric, making sync resume more reliable after interruptions.
    • Sync progress indicators and completion events now reflect committed filter progress, improving accuracy of displayed sync status and batch processing behavior.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 16, 2026

📝 Walkthrough

Walkthrough

Replaces wallet "synced height" usage with a new "filter committed height" concept across filter sync logic and wallet manager. Adds trait accessors and storage for filter_committed_height, updates initialization, progress checks, and commit paths to use committed height for filter batch lifecycle and restart recovery.

Changes

Cohort / File(s) Summary
Filter Manager / Sync
dash-spv/src/sync/filters/manager.rs, dash-spv/src/sync/filters/sync_manager.rs
Switched internal progress tracking and decision points from wallet.synced_height() to wallet.filter_committed_height()/self.committed_height; adjusted scan_start, batch commit, progress checks, and SyncEvent tip_height to use committed height; made committed_height field pub(super).
Wallet Interface Trait
key-wallet-manager/src/wallet_interface.rs
Added two trait methods: filter_committed_height() -> CoreBlockHeight and update_filter_committed_height(height) with default implementations delegating to existing synced-height accessors.
Wallet Manager Storage & Impl
key-wallet-manager/src/wallet_manager/mod.rs, key-wallet-manager/src/wallet_manager/process_block.rs
Added filter_committed_height field to WalletManager and initialized to 0; implemented trait getter and mutator that update internal filter_committed_height and call update_synced_height() when advancing.

Sequence Diagram

sequenceDiagram
    participant FM as Filter Manager
    participant WI as Wallet Interface
    participant WM as Wallet Manager
    FM->>WI: filter_committed_height()
    WI->>WM: filter_committed_height()
    WM-->>WI: return committed_height
    WI-->>FM: return committed_height
    FM->>FM: calculate scan_start / decide batches from committed_height
    FM->>FM: process filter batch
    FM->>WI: update_filter_committed_height(batch.end_height)
    WI->>WM: update_filter_committed_height(height)
    WM->>WM: set filter_committed_height = height
    WM->>WM: if height > synced_height -> update_synced_height(height)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 Hop-hop, I nibble through the log and byte,
I mark a committed height by moonlit night.
Batches snug in rows, checkpoints kept so neat,
Restarts find their place — the sync stays sweet. ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 63.64% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: introducing a separate filter_committed_height distinct from synced_height to fix restart recovery issues.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/filter-commited-height

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@dash-spv/src/sync/filters/manager.rs`:
- Around line 496-500: When committing the batch you must avoid regressing
synced_height: before calling
wallet.write().await.update_filter_committed_height(batch.end_height()) (in the
commit block that removes active_batches and sets
self.committed_height/self.processing_height), read the current synced height
from the wallet and only update the committed/synced value if batch.end_height()
does not move synced_height backward (e.g. use max(current_synced_height,
batch.end_height()) or skip the update when batch.end_height() <
current_synced_height). Mirror the guard used in process_block.rs so
update_filter_committed_height cannot cause synced_height to decrease.

In `@key-wallet-manager/src/wallet_manager/process_block.rs`:
- Around line 140-143: The method update_filter_committed_height currently sets
filter_committed_height and then unconditionally calls update_synced_height,
which can regress the tracked synced_height; change it so that after setting
self.filter_committed_height = height you only call
self.update_synced_height(height) if the incoming height is greater than the
current self.synced_height (i.e., avoid backward movement), referencing
update_filter_committed_height, update_synced_height, and the fields
synced_height and filter_committed_height.

@xdustinface xdustinface changed the title fix: separate filter scan committed height from per-block synced height fix: separate filter scan height from synced height Feb 16, 2026
@xdustinface xdustinface force-pushed the fix/filter-commited-height branch from 4a66511 to bb48156 Compare February 16, 2026 16:34
ZocoLini
ZocoLini previously approved these changes Feb 16, 2026
@xdustinface xdustinface force-pushed the fix/filter-commited-height branch from bb48156 to 7b60925 Compare February 17, 2026 13:20
@github-actions github-actions bot added the merge-conflict The PR conflicts with the target branch. label Feb 17, 2026
@github-actions
Copy link

This PR has merge conflicts with the base branch. Please rebase or merge the base branch into your branch to resolve them.

Introduces `filter_committed_height` on `WalletInterface`, separate from `synced_height`.
`process_block` updates `synced_height` per-block (needed for balance/maturity calculations),
but `FiltersManager` uses `filter_committed_height` for restart recovery. This prevents a
bug where per-block `synced_height` advances past uncommitted filter batches, causing the
rescan on restart to skip heights that need rescanning for newly discovered addresses.

It's not the best solution I think, hence I added the TODO for now. I will look into
this at some point later.
Sync completion checks in `start_download` and `check_sync_complete`
used `progress.current_height()` which tracks stored filter height,
not the height committed to wallet. This could lead to sync complete
before all batches were committed, causing skipped heights on restart.
@xdustinface xdustinface force-pushed the fix/filter-commited-height branch from 7b60925 to f563e33 Compare February 17, 2026 13:21
@github-actions github-actions bot removed the merge-conflict The PR conflicts with the target branch. label Feb 17, 2026
@xdustinface xdustinface requested a review from ZocoLini February 17, 2026 13:22
@xdustinface
Copy link
Collaborator Author

@ZocoLini Please re-review, i found a issue in this one here that i fixed in another commit, see f563e33.

@thepastaclaw
Copy link

thepastaclaw commented Feb 17, 2026

@coderabbitai review

(note this was the human, logged into the wrong account)

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 17, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 17, 2026

@thepastaclaw: I'll review the changes in this PR.


🧠 Learnings used
Learnt from: DCG-Claude
Repo: dashpay/rust-dashcore PR: 0
File: :0-0
Timestamp: 2025-06-26T16:01:37.609Z
Learning: The mempool tracking infrastructure (UnconfirmedTransaction, MempoolState, configuration, and mempool_filter.rs) is fully implemented and integrated in the Dash SPV client as of this PR, including client logic, FFI APIs, and tests.

Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv/CLAUDE.md:0-0
Timestamp: 2026-02-04T20:09:58.668Z
Learning: Applies to dash-spv/src/sync/**/*.rs : Synchronize blockchain headers in Phase 1, masternode list in Phase 2, filter headers in Phase 3, filters in Phase 4, and blocks in Phase 5
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
dash-spv/src/sync/filters/manager.rs (1)

151-156: Verify scan_start calculation when wallet_committed_height > 0 but less than wallet_birth_height.

If wallet_committed_height is between 1 and wallet_birth_height - 1 (e.g., due to data corruption or manual reset), then wallet_birth_height.max(wallet_committed_height + 1) correctly resolves to wallet_birth_height, so this is safe. However, this means a committed height below birth height is silently ignored — if this scenario is unexpected, a warning log could help debugging.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@dash-spv/src/sync/filters/manager.rs` around lines 151 - 156, The calculation
of scan_start using wallet_birth_height.max(wallet_committed_height +
1).max(header_start_height) can silently ignore a non-zero
wallet_committed_height that is less than wallet_birth_height; add an explicit
check before computing scan_start: if wallet_committed_height > 0 &&
wallet_committed_height < wallet_birth_height, emit a warning log (via the
existing logger used in this module) indicating the unexpected committed height
and the values of wallet_committed_height and wallet_birth_height, then proceed
with the current max-based computation; reference the variables scan_start,
wallet_committed_height, wallet_birth_height, and header_start_height to locate
where to insert the check and logging.
key-wallet-manager/src/wallet_manager/process_block.rs (1)

184-197: Consider adding test coverage for filter_committed_height.

The existing test_synced_height test validates synced_height but doesn't exercise the new filter_committed_height methods or verify the interaction between them (e.g., that update_filter_committed_height only advances synced_height when the new height exceeds it).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@key-wallet-manager/src/wallet_manager/process_block.rs` around lines 184 -
197, Add a new async test (e.g., test_filter_committed_height) that constructs a
WalletManager<ManagedWalletInfo> (Network::Testnet), asserts initial values for
synced_height() and filter_committed_height(), then call
update_filter_committed_height with increasing heights and verify that
filter_committed_height() advances accordingly and that synced_height() only
advances when the new filter committed height exceeds the current synced_height
(and never decreases when a smaller value is applied). Also include a case where
update_filter_committed_height is called with a lower height to ensure neither
filter_committed_height nor synced_height decreases, and a case where advancing
filter_committed_height causes synced_height to be bumped to the same value.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@dash-spv/src/sync/filters/manager.rs`:
- Around line 500-507: The commit logic already prevents regressing
committed_height: when removing a batch via active_batches.remove(&batch_start)
and computing end = batch.end_height(), the code only updates
self.committed_height and calls
wallet.write().await.update_filter_committed_height(end) if end >
self.committed_height, and processing_height is advanced to end + 1—no code
change required; keep the existing guard around committed_height and the call to
update_filter_committed_height as-is (they together prevent backward movement).

---

Nitpick comments:
In `@dash-spv/src/sync/filters/manager.rs`:
- Around line 151-156: The calculation of scan_start using
wallet_birth_height.max(wallet_committed_height + 1).max(header_start_height)
can silently ignore a non-zero wallet_committed_height that is less than
wallet_birth_height; add an explicit check before computing scan_start: if
wallet_committed_height > 0 && wallet_committed_height < wallet_birth_height,
emit a warning log (via the existing logger used in this module) indicating the
unexpected committed height and the values of wallet_committed_height and
wallet_birth_height, then proceed with the current max-based computation;
reference the variables scan_start, wallet_committed_height,
wallet_birth_height, and header_start_height to locate where to insert the check
and logging.

In `@key-wallet-manager/src/wallet_manager/process_block.rs`:
- Around line 184-197: Add a new async test (e.g., test_filter_committed_height)
that constructs a WalletManager<ManagedWalletInfo> (Network::Testnet), asserts
initial values for synced_height() and filter_committed_height(), then call
update_filter_committed_height with increasing heights and verify that
filter_committed_height() advances accordingly and that synced_height() only
advances when the new filter committed height exceeds the current synced_height
(and never decreases when a smaller value is applied). Also include a case where
update_filter_committed_height is called with a lower height to ensure neither
filter_committed_height nor synced_height decreases, and a case where advancing
filter_committed_height causes synced_height to be bumped to the same value.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
key-wallet-manager/src/wallet_manager/process_block.rs (1)

183-197: Consider adding test coverage for the new filter_committed_height methods.

The existing test_synced_height test validates synced_height behavior but doesn't cover the new filter_committed_height / update_filter_committed_height methods. A small test verifying the decoupled behavior (e.g., that update_filter_committed_height doesn't regress synced_height) would increase confidence.

💡 Example test
#[tokio::test]
async fn test_filter_committed_height() {
    let mut manager: WalletManager<ManagedWalletInfo> = WalletManager::new(Network::Testnet);
    assert_eq!(manager.filter_committed_height(), 0);

    // Advance synced_height via process_block simulation
    manager.update_synced_height(1000);
    assert_eq!(manager.synced_height(), 1000);

    // filter_committed_height should be independent
    assert_eq!(manager.filter_committed_height(), 0);

    // Committing a lower filter height should NOT regress synced_height
    manager.update_filter_committed_height(500);
    assert_eq!(manager.filter_committed_height(), 500);
    assert_eq!(manager.synced_height(), 1000); // unchanged

    // Committing a higher filter height should also advance synced_height
    manager.update_filter_committed_height(2000);
    assert_eq!(manager.filter_committed_height(), 2000);
    assert_eq!(manager.synced_height(), 2000);
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@key-wallet-manager/src/wallet_manager/process_block.rs` around lines 183 -
197, Add a new unit test mirroring test_synced_height to cover the new
filter_committed_height behavior on WalletManager<ManagedWalletInfo>: verify
initial filter_committed_height() == 0, that calling update_synced_height(...)
does not change filter_committed_height, that
update_filter_committed_height(lower) updates filter_committed_height without
regressing synced_height(), and that update_filter_committed_height(higher)
advances both filter_committed_height() and synced_height() as expected; place
the test alongside test_synced_height in process_block.rs and reference the
WalletManager, update_synced_height, update_filter_committed_height,
filter_committed_height, and synced_height methods.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@key-wallet-manager/src/wallet_manager/process_block.rs`:
- Around line 183-197: Add a new unit test mirroring test_synced_height to cover
the new filter_committed_height behavior on WalletManager<ManagedWalletInfo>:
verify initial filter_committed_height() == 0, that calling
update_synced_height(...) does not change filter_committed_height, that
update_filter_committed_height(lower) updates filter_committed_height without
regressing synced_height(), and that update_filter_committed_height(higher)
advances both filter_committed_height() and synced_height() as expected; place
the test alongside test_synced_height in process_block.rs and reference the
WalletManager, update_synced_height, update_filter_committed_height,
filter_committed_height, and synced_height methods.

@xdustinface xdustinface merged commit 1e732db into v0.42-dev Feb 17, 2026
53 checks passed
@xdustinface xdustinface deleted the fix/filter-commited-height branch February 17, 2026 15:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants